package services

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/url"
	"time"

	"cashew.com/cashew_api/dtos"
	"cashew.com/cashew_common/configs"
	"cashew.com/cashew_common/constants"
	"cashew.com/cashew_common/utils"
	"github.com/beego/beego/v2/core/logs"
)

// 0:非通话中变更 1:通话中变更
type TagType int

const (
	TAG_TYPE_NON_CALL_CHANGE TagType = 0
	TAG_TYPE_CALL_CHANGE     TagType = 1
)

// MsgBody 对应整体的msgBody结构
type MsgBody struct {
	Code int        `json:"code"`
	Data SubMsgData `json:"data"`
}

// SubMsgData 对应msgBody中"data"字段内的结构
type SubMsgData struct {
	SubCode int             `json:"sub_code"`
	Data    ChatDataDetails `json:"data"`
}

// ChatDataDetails 对应 SubMsgData 中"data"字段内的结构
type ChatDataDetails struct {
	FromUserID     string     `json:"from_user_id"`
	ToUserID       string     `json:"to_user_id"`
	FromType       int        `json:"from_type"`
	MsgType        int        `json:"msg_type"`
	Msg            string     `json:"msg"`
	Duration       int        `json:"duration"`
	CreateTime     int64      `json:"create_time"`
	ChatScenario   int        `json:"chat_scenario"`
	FromUserDetail UserDetail `json:"from_user_detail"`
	ToUserDetail   UserDetail `json:"to_user_detail"`
}

type ImChatMQ struct {
	ChatInitType int             `json:"chat_init_type"` // 聊天发起类型： 0: 主播发起  1: 用户发起 2:系统发起
	MsgType      int             `json:"msg_type"`       // 消息类型 0：在线消息 1：离线消息
	ScriptId     string          `json:"script_id"`
	Data         ChatDataDetails `json:"data"`
}

type ImChatRequest struct {
	NoticeUserId   string
	IsOnline       bool
	FromType       int
	MsgType        int
	Msg            string
	Duration       int
	ChatScenario   int
	Identity       int
	FromUserDetail UserDetail
	ToUserDetail   UserDetail
}

func fillUserDetail(userDetail *UserDetail, userInfo *UserCache) {
	if userInfo != nil {
		userDetail.UserId = fmt.Sprintf("%d", userInfo.UserId)
		userDetail.Nick = userInfo.Nick
		if userInfo.HeadImg != "" && userInfo.HeadImg != "null" {
			userDetail.HeadImg = userInfo.HeadImg
		}
		userDetail.Age = userInfo.Age
		if userInfo.FlagImage != "" && userInfo.FlagImage != "null" {
			userDetail.FlagImage = CompleteURL(userInfo.FlagImage)
		}
		userDetail.Gender = userInfo.Gender
	}
}

func AssembleImChatRequest(params *dtos.SendChatParams, identity int, noticeUserId string, isOnline bool, viewerInfo *UserCache, anchorInfo *UserCache) ImChatRequest {
	req := ImChatRequest{
		NoticeUserId: noticeUserId,
		IsOnline:     isOnline,
		FromType:     params.FromType,
		MsgType:      params.MsgType,
		Msg:          params.Msg,
		Duration:     params.Duration,
		ChatScenario: params.ChatScenario,
		Identity:     identity,
	}

	if params.MsgType == constants.CHAT_MSG_TYPE_PIC || params.MsgType == constants.CHAT_MSG_TYPE_AUDIO || params.MsgType == constants.CHAT_MSG_TYPE_VIDEO {
		req.Msg = CompleteURL(req.Msg)
	}

	if identity == constants.IDENTITY_NORMAL_USER {
		fillUserDetail(&req.FromUserDetail, viewerInfo)
		fillUserDetail(&req.ToUserDetail, anchorInfo)
	} else {
		fillUserDetail(&req.FromUserDetail, anchorInfo)
		fillUserDetail(&req.ToUserDetail, viewerInfo)
	}

	return req
}

func AssembleImChatRequest2(params *dtos.SendChatParams, identity int, noticeUserId string, isOnline bool, fromUserCacheInfo *UserCache, toUserCacheInfo *UserCache) ImChatRequest {
	req := ImChatRequest{
		NoticeUserId: noticeUserId,
		IsOnline:     isOnline,
		FromType:     params.FromType,
		MsgType:      params.MsgType,
		Msg:          params.Msg,
		Duration:     params.Duration,
		ChatScenario: params.ChatScenario,
		Identity:     identity,
	}

	if params.MsgType == constants.CHAT_MSG_TYPE_PIC || params.MsgType == constants.CHAT_MSG_TYPE_AUDIO || params.MsgType == constants.CHAT_MSG_TYPE_VIDEO {
		req.Msg = CompleteURL(req.Msg)
	}

	fillUserDetail(&req.FromUserDetail, fromUserCacheInfo)
	fillUserDetail(&req.ToUserDetail, toUserCacheInfo)

	return req
}

func ImChat(req *ImChatRequest) (err error) {
	// 组装消息体数据
	msgBody := MsgBody{
		Code: constants.CHAT_CODE_IM,
		Data: SubMsgData{
			SubCode: constants.CHAT_CODE_IM_CHAT,
			Data: ChatDataDetails{
				FromUserID:     req.FromUserDetail.UserId,
				ToUserID:       req.ToUserDetail.UserId,
				FromType:       req.FromType,
				MsgType:        req.MsgType,
				Msg:            req.Msg,
				Duration:       req.Duration,
				CreateTime:     time.Now().Unix(),
				ChatScenario:   req.ChatScenario,
				FromUserDetail: req.FromUserDetail,
				ToUserDetail:   req.ToUserDetail,
			},
		},
	}

	// 判断发离线还是在线消息
	if req.IsOnline {
		// 调用IM接口将消息发送给对方
		postUrl := configs.WsHttpUrl
		headers := map[string]string{
			"Content-Type": "application/json",
		}
		queryParams := url.Values{}
		//queryParams.Set("userid", req.ToUserId)
		queryParams.Set("userid", req.NoticeUserId)
		queryParams.Set("service_code", "cashew_api")
		queryParams.Set("author_code", fmt.Sprintf("%d", constants.CHAT_CODE_IM))

		// 构建完整的请求URL，将查询参数添加到URL中
		url := postUrl + "?" + queryParams.Encode()

		respBody, errH := HttpCall("POST", url, headers, msgBody)
		if errH != nil {
			logs.Error("向 userId：%s 送在线消息 HttpCall(%s, %s, %s, %s):%v", req.NoticeUserId, "POST", url, headers, msgBody, errH)
			return errH
		}
		logs.Debug("响应体数据:%s", string(respBody))
		logs.Info("已发向 userId：%s 送在线消息 msgBody:%+v", req.NoticeUserId, msgBody)
	} else {
		// 将消息存redis队列，待对方上线后接收
		// 将结构体转换为JSON字符串
		jsonStr, errJ := json.Marshal(msgBody)
		if errJ != nil {
			logs.Error("json.Marshal(%+v) failed:%s", msgBody, errJ)
			return errJ
		}

		// 输出JSON字符串
		redisserver := new(utils.RedisService)
		queueName := "CASHEW::IM::USER::" + req.NoticeUserId
		errL := redisserver.RPush(queueName, string(jsonStr))
		if errL != nil {
			logs.Error("redisserver.RPush(%s, %s)失败:%v", queueName, string(jsonStr), errL)
			return errL
		}
		logs.Info("已发向 userId：%s 送离线消息 msgBody:%+v", req.NoticeUserId, msgBody)
	}

	// 消息加入mq
	imChatMQ := ImChatMQ{
		ChatInitType: constants.CHAT_INIT_TYPE_ANCHOR,
		MsgType:      constants.MQ_MSG_TYPE_ONLINE,
		ScriptId:     "0",
		Data:         msgBody.Data.Data,
	}
	if !req.IsOnline {
		imChatMQ.MsgType = constants.MQ_MSG_TYPE_OFFLINE
	}
	if req.FromType == constants.MSG_FROM_TYPE_SYS {
		imChatMQ.ChatInitType = constants.CHAT_INIT_TYPE_SYS
	} else if req.Identity == constants.IDENTITY_NORMAL_USER {
		imChatMQ.ChatInitType = constants.CHAT_INIT_TYPE_USER
	}
	jsonStr, errJ := json.Marshal(imChatMQ)
	if errJ != nil {
		logs.Error("json.Marshal(%+v)失败:%s", imChatMQ, errJ)
		return errJ
	}

	logs.Info("正在发送消息到mq:%s", string(jsonStr))
	utils.GetRabbitMq().Publish("cashew.chat.record.notice", string(jsonStr))
	logs.Info("PublishMsgToMq success ！")

	return
}

func AssembleImSendMessageRequest(params *dtos.SendMessageParams, identity int, noticeUserId string, isOnline bool) ImSendMessageRequest {
	req := ImSendMessageRequest{
		NoticeUserId:   noticeUserId,
		IsOnline:       isOnline,
		MsgType:        params.MsgType,
		Msg:            params.Msg,
		Duration:       params.Duration,
		ChatScenario:   params.ChatScenario,
		Identity:       identity,
		FromUserDetail: params.UserInfo,
		ToUserDetail:   params.ToUserInfo,
	}

	if params.MsgType == constants.CHAT_MSG_TYPE_PIC || params.MsgType == constants.CHAT_MSG_TYPE_AUDIO || params.MsgType == constants.CHAT_MSG_TYPE_VIDEO {
		req.Msg = CompleteURL(req.Msg)
	}

	return req
}

type ImSendMessageRequest struct {
	NoticeUserId   string
	IsOnline       bool
	FromType       int
	MsgType        int
	Msg            string
	Duration       int
	ChatScenario   int
	Identity       int
	FromUserDetail *dtos.UserInfo
	ToUserDetail   *dtos.UserInfo
}

func ImSendMessage(req *ImSendMessageRequest) (err error) {
	// 组装消息体数据
	msgBody := MsgBody{
		Code: constants.CHAT_CODE_IM,
		Data: SubMsgData{
			SubCode: constants.CHAT_CODE_IM_CHAT,
			Data: ChatDataDetails{
				FromUserID:   req.FromUserDetail.UserID,
				ToUserID:     req.ToUserDetail.UserID,
				FromType:     req.FromType,
				MsgType:      req.MsgType,
				Msg:          req.Msg,
				Duration:     req.Duration,
				CreateTime:   time.Now().Unix(),
				ChatScenario: req.ChatScenario,
				FromUserDetail: UserDetail{
					UserId:    req.FromUserDetail.UserID,
					Nick:      req.FromUserDetail.Nick,
					HeadImg:   req.FromUserDetail.HeadImg,
					Age:       req.FromUserDetail.Age,
					Gender:    req.FromUserDetail.Gender,
					FlagImage: CompleteURL(req.FromUserDetail.FlagImage),
				},
				ToUserDetail: UserDetail{
					UserId:    req.ToUserDetail.UserID,
					Nick:      req.ToUserDetail.Nick,
					HeadImg:   req.ToUserDetail.HeadImg,
					Age:       req.ToUserDetail.Age,
					Gender:    req.ToUserDetail.Gender,
					FlagImage: CompleteURL(req.ToUserDetail.FlagImage),
				},
			},
		},
	}

	// 判断发离线还是在线消息
	if req.IsOnline {
		// 调用IM接口将消息发送给对方
		postUrl := configs.WsHttpUrl
		headers := map[string]string{
			"Content-Type": "application/json",
		}
		queryParams := url.Values{}
		//queryParams.Set("userid", req.ToUserId)
		queryParams.Set("userid", req.NoticeUserId)
		queryParams.Set("service_code", "cashew_api")
		queryParams.Set("author_code", fmt.Sprintf("%d", constants.CHAT_CODE_IM))

		// 构建完整的请求URL，将查询参数添加到URL中
		url := postUrl + "?" + queryParams.Encode()

		respBody, errH := HttpCall("POST", url, headers, msgBody)
		if errH != nil {
			logs.Error("向 userId：%s 送在线消息 HttpCall(%s, %s, %s, %s):%v", req.NoticeUserId, "POST", url, headers, msgBody, errH)
			return errH
		}
		logs.Debug("响应体数据:%s", string(respBody))
		logs.Info("已发向 userId：%s 送在线消息 msgBody:%+v", req.NoticeUserId, msgBody)
	} else {
		// 将消息存redis队列，待对方上线后接收
		// 将结构体转换为JSON字符串
		jsonStr, errJ := json.Marshal(msgBody)
		if errJ != nil {
			logs.Error("json.Marshal(%+v) failed:%s", msgBody, errJ)
			return errJ
		}

		// 输出JSON字符串
		redisserver := new(utils.RedisService)
		queueName := "CASHEW::IM::USER::" + req.NoticeUserId
		errL := redisserver.RPush(queueName, string(jsonStr))
		if errL != nil {
			logs.Error("redisserver.RPush(%s, %s)失败:%v", queueName, string(jsonStr), errL)
			return errL
		}
		logs.Info("已发向 userId：%s 送离线消息 msgBody:%+v", req.NoticeUserId, msgBody)
	}

	// 消息加入mq
	imChatMQ := ImChatMQ{
		ChatInitType: constants.CHAT_INIT_TYPE_ANCHOR,
		MsgType:      constants.MQ_MSG_TYPE_ONLINE,
		ScriptId:     "0",
		Data:         msgBody.Data.Data,
	}
	if !req.IsOnline {
		imChatMQ.MsgType = constants.MQ_MSG_TYPE_OFFLINE
	}
	if req.FromType == constants.MSG_FROM_TYPE_SYS {
		imChatMQ.ChatInitType = constants.CHAT_INIT_TYPE_SYS
	} else if req.Identity == constants.IDENTITY_NORMAL_USER {
		imChatMQ.ChatInitType = constants.CHAT_INIT_TYPE_USER
	}
	jsonStr, errJ := json.Marshal(imChatMQ)
	if errJ != nil {
		logs.Error("json.Marshal(%+v)失败:%s", imChatMQ, errJ)
		return errJ
	}

	logs.Info("正在发送消息到mq:%s", string(jsonStr))
	utils.GetRabbitMq().Publish("cashew.chat.record.notice", string(jsonStr))
	logs.Info("PublishMsgToMq success ！")

	return
}

func ReceiveOfflineMessagesOnUserOnline(NoticeUserId string, msgBody interface{}) (err error) {
	// 调用IM接口将消息发送给对方
	postUrl := configs.WsHttpUrl
	headers := map[string]string{
		"Content-Type": "application/json",
	}
	queryParams := url.Values{}
	queryParams.Set("userid", NoticeUserId)
	queryParams.Set("service_code", "cashew_api")
	queryParams.Set("author_code", fmt.Sprintf("%d", constants.CHAT_CODE_IM))

	// 构建完整的请求URL，将查询参数添加到URL中
	fullUrl := postUrl + "?" + queryParams.Encode()

	respBody, errH := HttpCall("POST", fullUrl, headers, msgBody)
	if errH != nil {
		logs.Error("向新上线用户 userId：%s 发送离线消息 HttpCall(%s, %s, %s, %s):%v", NoticeUserId, "POST", fullUrl, headers, msgBody, errH)
		return errH
	}
	logs.Debug("响应体数据:%s", string(respBody))
	logs.Info("已成功向新上线用户 userId：%s 发送离线消息 msgBody:%+v", NoticeUserId, msgBody)

	return
}

type ImSendGiftRequest struct {
	NoticeUserId   string
	IsOnline       bool
	FromUserId     string
	FromType       int
	ToUserId       string
	PropId         int
	PropNum        int
	ChatScenario   int
	Odds           Odds
	FromUserDetail UserDetail
	ToUserDetail   UserDetail
}

func AssembleImSendGiftRequest(params *dtos.SendGiftParams, identity int, noticeUserId string, isOnline bool, odds *Odds, viewerInfo *UserCache, anchorInfo *UserCache) ImSendGiftRequest {
	req := ImSendGiftRequest{
		NoticeUserId: noticeUserId,
		IsOnline:     isOnline,
		FromUserId:   params.UserIdstr,
		ToUserId:     params.ToUserId,
		FromType:     0,
		PropId:       params.PropId,
		PropNum:      params.PropNum,
		ChatScenario: params.ChatScenario,
		Odds:         *odds,
	}

	//if odds != nil {
	//	req.Odds = *odds
	//}

	if identity == constants.IDENTITY_NORMAL_USER {
		fillUserDetail(&req.FromUserDetail, viewerInfo)
		fillUserDetail(&req.ToUserDetail, anchorInfo)
	} else {
		fillUserDetail(&req.FromUserDetail, anchorInfo)
		fillUserDetail(&req.ToUserDetail, viewerInfo)
	}

	return req
}

func AssembleImSendGiftRequest2(params *dtos.SendGiftParams, noticeUserId string, isOnline bool, odds *Odds, fromUserCacheInfo *UserCache, toUserCacheInfo *UserCache) ImSendGiftRequest {
	req := ImSendGiftRequest{
		NoticeUserId: noticeUserId,
		IsOnline:     isOnline,
		FromUserId:   params.UserIdstr,
		ToUserId:     params.ToUserId,
		FromType:     0,
		PropId:       params.PropId,
		PropNum:      params.PropNum,
		ChatScenario: params.ChatScenario,
		Odds:         *odds,
	}

	fillUserDetail(&req.FromUserDetail, fromUserCacheInfo)
	fillUserDetail(&req.ToUserDetail, toUserCacheInfo)

	return req
}

func ImSendGift(req *ImSendGiftRequest) (err error) {
	// 组装消息体数据
	var msgBody = map[string]interface{}{
		"from_user_id":     req.FromUserId,
		"to_user_id":       req.ToUserId,
		"from_type":        0,
		"prop_id":          req.PropId,
		"prop_num":         req.PropNum,
		"create_time":      time.Now().Unix(),
		"chat_scenario":    req.ChatScenario,
		"from_user_detail": req.FromUserDetail,
		"to_user_detail":   req.ToUserDetail,
		"odds":             req.Odds,
	}

	reqBody := map[string]interface{}{
		"code": constants.CHAT_CODE_IM,
		"data": map[string]interface{}{
			"sub_code": constants.CHAT_CODE_IM_GIFT,
			"data":     msgBody,
		},
	}

	// 判断发离线还是在线消息
	if req.IsOnline {
		// 调用IM接口将消息发送给对方
		postUrl := configs.WsHttpUrl
		headers := map[string]string{
			"Content-Type": "application/json",
		}
		queryParams := url.Values{}
		queryParams.Set("userid", req.NoticeUserId)
		queryParams.Set("service_code", "cashew_api")
		queryParams.Set("author_code", fmt.Sprintf("%d", constants.CHAT_CODE_IM))

		// 构建完整的请求URL，将查询参数添加到URL中
		url := postUrl + "?" + queryParams.Encode()

		// 判断发给自己还是给对面，如果是发给自己的，则要带上中奖信息
		respBody, errHttp := HttpCall("POST", url, headers, reqBody)
		if errHttp != nil {
			return errors.New(errHttp.Error())
		}
		logs.Debug("响应体数据:%s", string(respBody))
		logs.Info("已发向 userId：%s 送在线消息 msgBody:%+v", req.NoticeUserId, msgBody)
	} else {
		// 将消息存redis队列，待对方上线后接收
		// 将结构体转换为JSON字符串

		jsonStr, errJ := json.Marshal(msgBody)
		if errJ != nil {
			logs.Warn("json.Marshal(%+v) failed:%s", msgBody, errJ)
			return errJ
		}

		// 输出JSON字符串
		logs.Debug("转换后的JSON字符串:%s", string(jsonStr))

		redisserver := new(utils.RedisService)
		queueName := "CASHEW::IM::USER::" + req.NoticeUserId
		errL := redisserver.RPush(queueName, string(jsonStr))
		if errL != nil {
			logs.Error("redisserver.LPush(%s, %s)失败:%v", queueName, string(jsonStr), errL)
			return errL
		}
		logs.Info("已发向 userId：%s 送离线消息 msgBody:%+v", req.NoticeUserId, msgBody)
	}

	return
}

type UserDetail struct {
	UserId    string `json:"user_id"`
	Nick      string `json:"nick"`
	HeadImg   string `json:"head_img"`
	Age       int    `json:"age"`
	FlagImage string `json:"flag_image"`
	Gender    int    `json:"gender"`
}

type ImVideocallCallRequest struct {
	NoticeUserId   string
	CallType       int
	PayPrice       int64
	AnchorPrice    int64
	AnchorId       int64
	CallerId       int64
	IsFreeCard     bool
	FreeTime       int
	FromUserDetail UserDetail
	ToUserDetail   UserDetail
}

func ImVideocallCall(req *ImVideocallCallRequest) (err error) {
	postUrl := configs.WsHttpUrl
	headers := map[string]string{
		"Content-Type": "application/json",
	}
	queryParams := url.Values{}
	queryParams.Set("userid", req.NoticeUserId)
	queryParams.Set("service_code", "cashew_api")
	queryParams.Set("author_code", fmt.Sprintf("%d", constants.CHAT_CODE_VIDEOCALL))

	// 构建完整的请求URL，将查询参数添加到URL中
	reqUrl := postUrl + "?" + queryParams.Encode()
	reqBody := map[string]interface{}{
		"code": constants.CHAT_CODE_VIDEOCALL,
		"data": map[string]interface{}{
			"sub_code": constants.CHAT_CODE_VIDEOCALL_CALL,
			"data": map[string]interface{}{
				"from_user_id":     req.FromUserDetail.UserId,
				"to_user_id":       req.ToUserDetail.UserId,
				"call_type":        req.CallType,
				"pay_price":        fmt.Sprintf("%d", req.PayPrice),
				"anchor_price":     fmt.Sprintf("%d", req.AnchorPrice),
				"anchor_id":        fmt.Sprintf("%d", req.AnchorId),
				"is_free_card":     req.IsFreeCard,
				"free_time":        req.FreeTime,
				"from_user_detail": req.FromUserDetail,
				"to_user_detail":   req.ToUserDetail,
				//"caller_id":        fmt.Sprintf("%d", req.CallerId),
			},
		},
	}
	// 暂无
	respBody, err := HttpCall("POST", reqUrl, headers, reqBody)
	if err != nil {
		return
	}
	logs.Debug("响应体数据:%s", string(respBody))
	return
}

type ImVideocallAnswerRequest struct {
	NoticeUserId   string
	Guid           string
	StreamId       string
	HomeNum        string
	AnchorId       int64
	PayPrice       int64
	AnchorPrice    int64
	FromUserDetail UserDetail
	ToUserDetail   UserDetail
}

func ImVideocallAnswer(req *ImVideocallAnswerRequest) (err error) {
	postUrl := configs.WsHttpUrl
	headers := map[string]string{
		"Content-Type": "application/json",
	}

	queryParams := url.Values{}
	queryParams.Set("userid", req.NoticeUserId)
	queryParams.Set("service_code", "cashew_api")
	queryParams.Set("author_code", fmt.Sprintf("%d", constants.CHAT_CODE_VIDEOCALL))

	// 构建完整的请求URL，将查询参数添加到URL中
	url := postUrl + "?" + queryParams.Encode()

	reqBody := map[string]interface{}{
		"code": constants.CHAT_CODE_VIDEOCALL,
		"data": map[string]interface{}{
			"sub_code": constants.CHAT_CODE_VIDEOCALL_ANSWER,
			"data": map[string]interface{}{
				"from_user_id":     req.FromUserDetail.UserId,
				"to_user_id":       req.ToUserDetail.UserId,
				"guid":             req.Guid,
				"stream_id":        req.StreamId,
				"home_num":         req.HomeNum,
				"pay_price":        fmt.Sprintf("%d", req.PayPrice),
				"anchor_price":     fmt.Sprintf("%d", req.AnchorPrice),
				"anchor_id":        fmt.Sprintf("%d", req.AnchorId),
				"from_user_detail": req.FromUserDetail,
				"to_user_detail":   req.ToUserDetail,
			},
		},
	}
	respBody, err := HttpCall("POST", url, headers, reqBody)
	if err != nil {
		return
	}
	logs.Debug("响应体数据:%s", string(respBody))
	return
}

type ImConfirmCallRequest struct {
	NoticeUserId   string
	Guid           string
	StreamId       string
	HomeNum        string
	AnchorId       int64
	FromUserDetail UserDetail
	ToUserDetail   UserDetail
}

func ImConfirmCall(req *ImConfirmCallRequest) (err error) {
	postUrl := configs.WsHttpUrl
	headers := map[string]string{
		"Content-Type": "application/json",
	}
	queryParams := url.Values{}
	queryParams.Set("userid", req.NoticeUserId)
	queryParams.Set("service_code", "cashew_api")
	queryParams.Set("author_code", fmt.Sprintf("%d", constants.CHAT_CODE_VIDEOCALL))

	// 构建完整的请求URL，将查询参数添加到URL中
	url := postUrl + "?" + queryParams.Encode()
	reqBody := map[string]interface{}{
		"code": constants.CHAT_CODE_VIDEOCALL,
		"data": map[string]interface{}{
			"sub_code": constants.CHAT_CODE_VIDEOCALL_CALLING,
			"data": map[string]interface{}{
				"from_user_id":     req.FromUserDetail.UserId,
				"to_user_id":       req.ToUserDetail.UserId,
				"guid":             req.Guid,
				"stream_id":        req.StreamId,
				"home_num":         req.HomeNum,
				"anchor_id":        fmt.Sprintf("%d", req.AnchorId),
				"from_user_detail": req.FromUserDetail,
				"to_user_detail":   req.ToUserDetail,
			},
		},
	}
	respBody, err := HttpCall("POST", url, headers, reqBody)
	if err != nil {
		return
	}
	logs.Debug("响应体数据:%s", string(respBody))
	return
}

type ImVideocallHangupRequest struct {
	NoticeUserId   string
	Guid           string
	AnchorId       int64
	OperationType  int
	CallDuration   int64
	FromUserDetail UserDetail
	ToUserDetail   UserDetail
}

func ImVideocallHangup(req *ImVideocallHangupRequest) (err error) {
	postUrl := configs.WsHttpUrl
	headers := map[string]string{
		"Content-Type": "application/json",
	}
	queryParams := url.Values{}
	queryParams.Set("userid", req.NoticeUserId)
	queryParams.Set("service_code", "cashew_api")
	queryParams.Set("author_code", fmt.Sprintf("%d", constants.CHAT_CODE_VIDEOCALL))

	// 构建完整的请求URL，将查询参数添加到URL中
	url := postUrl + "?" + queryParams.Encode()
	dataMsg := map[string]interface{}{
		"from_user_id":     req.FromUserDetail.UserId,
		"to_user_id":       req.ToUserDetail.UserId,
		"guid":             req.Guid,
		"anchor_id":        fmt.Sprintf("%d", req.AnchorId),
		"operation_type":   req.OperationType,
		"call_duration":    req.CallDuration,
		"from_user_detail": req.FromUserDetail,
		"to_user_detail":   req.ToUserDetail,
	}

	dataSubCode := map[string]interface{}{
		"sub_code": constants.CHAT_CODE_VIDEOCALL_HANGUP,
		"data":     dataMsg,
	}
	reqBody := map[string]interface{}{
		"code": constants.CHAT_CODE_VIDEOCALL,
		"data": dataSubCode,
	}

	respBody, err := HttpCall("POST", url, headers, reqBody)
	if err != nil {
		return
	}
	logs.Debug("响应体数据:%s", string(respBody))
	return
}

type ImUserAmountChangeMsgRequest struct {
	NoticeUserId     string `json:"Notice_UserId"`
	UserId           string `json:"user_id"`
	Amount           string `json:"amount"`
	BalanceNotEnough int    `json:"balance_not_enough"`
	Msg              string `json:"msg"`
}

type UserAmountChangeMsg struct {
	UserId           string `json:"user_id"`
	Amount           string `json:"amount"`
	BalanceNotEnough int    `json:"balance_not_enough"`
	Msg              string `json:"msg"`
}

func ImUserAmountChangeMsg(req *ImUserAmountChangeMsgRequest) (err error) {
	logs.Debug("IM 通知用户 userId:%s 余额变更请求：ImUserAmountChangeMsg(%+v)", req.NoticeUserId, req)
	postUrl := configs.WsHttpUrl
	headers := map[string]string{
		"Content-Type": "application/json",
	}
	queryParams := url.Values{}
	queryParams.Set("userid", req.NoticeUserId)
	queryParams.Set("service_code", "cashew_api")
	queryParams.Set("author_code", fmt.Sprintf("%d", constants.CHAT_CODE_VIDEOCALL))

	// 构建完整的请求URL，将查询参数添加到URL中
	url := postUrl + "?" + queryParams.Encode()
	dataMsg := UserAmountChangeMsg{
		UserId:           req.UserId,
		Amount:           req.Amount,
		BalanceNotEnough: req.BalanceNotEnough,
		Msg:              req.Msg,
	}

	dataSubCode := map[string]interface{}{
		"sub_code": constants.CHAT_CODE_VIEWER_AMOUNT_CHANGE,
		"data":     dataMsg,
	}
	reqBody := map[string]interface{}{
		"code": constants.CHAT_CODE_VIDEOCALL,
		"data": dataSubCode,
	}

	respBody, err := HttpCall("POST", url, headers, reqBody)
	if err != nil {
		return
	}
	logs.Debug("响应体数据:%s", string(respBody))
	logs.Info("IM 通知用户 userId:%s 余额变更成功：ImUserAmountChangeMsg(%+v)", req.NoticeUserId, reqBody)
	return
}

type UserWrongMsg struct {
	ErrorNo constants.ErrNo `json:"error_no"`
	Msg     string          `json:"msg"`
}

// 通知用户报错信息
func ImUserWrongMsg(NoticeUserId string, errorNo constants.ErrNo, msg string) (err error) {
	logs.Debug("IM 通知用户 userId:%s 快速拨打电话失败：ImUserWrongMsg(%+v)", NoticeUserId)
	postUrl := configs.WsHttpUrl
	headers := map[string]string{
		"Content-Type": "application/json",
	}
	queryParams := url.Values{}
	queryParams.Set("userid", NoticeUserId)
	queryParams.Set("service_code", "cashew_api")
	queryParams.Set("author_code", fmt.Sprintf("%d", constants.CHAT_CODE_VIDEOCALL))

	// 构建完整的请求URL，将查询参数添加到URL中
	url := postUrl + "?" + queryParams.Encode()
	dataMsg := UserWrongMsg{
		ErrorNo: errorNo,
		Msg:     msg,
	}

	dataSubCode := map[string]interface{}{
		"sub_code": constants.CHAT_CODE_WRONG_MSG,
		"data":     dataMsg,
	}
	reqBody := map[string]interface{}{
		"code": constants.CHAT_CODE_VIDEOCALL,
		"data": dataSubCode,
	}

	respBody, err := HttpCall("POST", url, headers, reqBody)
	if err != nil {
		return
	}
	logs.Debug("响应体数据:%s", string(respBody))
	logs.Info("IM 通知用户 userId:%s 余额变更成功：ImUserAmountChangeMsg(%+v)", NoticeUserId, reqBody)
	return
}

// 发送用户币值变更消息
func SendUserBalanceChangeNotice(noticeUserId string, userId string, amount string, balanceNotEnough int, msg string) {
	reqUserAmountChangeMsg := AssembleImUserAmountChangeMsgRequest(noticeUserId, userId, amount, balanceNotEnough, msg)
	// 发送IM用户币值变更消息通知，并处理可能出现的错误
	err := ImUserAmountChangeMsg(&reqUserAmountChangeMsg)
	if err != nil {
		// 记录IM通知失败的错误日志，这里假设c是用于记录日志等操作的相关对象（需根据实际情况调整）
		logs.Error("IM 通知失败：ImUserAmountChangeMsg(%+v): %v", reqUserAmountChangeMsg, err)
		return
	}
	return
}

// 发送用户币值变更消息(模拟)
func SendUserBalanceChangeNotice2(userId int64) {
	// 创建一个每隔1秒触发一次的定时器
	ticker := time.NewTicker(3 * time.Second)
	defer ticker.Stop()

	startTime := time.Now()
	for {
		// 每次触发时执行一些耗时操作，比如打印信息（可替换为真实业务逻辑）
		logs.Info("我是用户 userId:%d 的 SendUserBalanceChangeNotice2 协程", userId)
		// 模拟一些复杂计算，这里简单用一个自增变量做耗时操作示例
		var sum int
		for i := 0; i < 10000000; i++ {
			sum += i
		}

		// 判断是否达到模拟的处理时间，比如这里模拟最长处理60秒
		if time.Since(startTime) > 60*time.Second {
			break
		}

		// 等待下一次定时器触发
		<-ticker.C
	}
	return
}

func AssembleImUserAmountChangeMsgRequest(noticeUserId string, userId string, amount string, balanceNotEnough int, msg string) ImUserAmountChangeMsgRequest {
	req := ImUserAmountChangeMsgRequest{
		NoticeUserId:     noticeUserId,
		UserId:           userId,
		Amount:           amount,
		BalanceNotEnough: balanceNotEnough,
		Msg:              msg,
	}

	return req
}

// 在utils.RedisService结构体中添加获取指定用户所有离线消息的方法
// 定义一个结构体来匹配消息中的数据结构
type MessageData struct {
	FromUserID string `json:"from_user_id"`
	ToUserID   string `json:"to_user_id"`
	FromType   string `json:"from_type"`
	MsgType    string `json:"msg_type"`
	Msg        string `json:"msg"`
	Duration   int64  `json:"duration"`
	CreateTime int64  `json:"create_time"`
}

type ChatMessage struct {
	Code int         `json:"code"`
	Data MessageData `json:"data"`
}

// 在utils.RedisService结构体中添加获取指定用户所有离线消息的方法
func GetAllOfflineMessages(userId string) ([]map[string]interface{}, error) {
	queueName := "CASHEW::IM::USER::" + userId

	// 使用LRANGE命令获取列表中的所有元素，这里起始索引为0，结束索引为-1表示获取全部元素
	result, err := utils.Redis.LRange(queueName, 0, -1).Result()
	if err != nil {
		return nil, err
	}

	var messages []map[string]interface{}
	for _, msgStr := range result {
		var msg map[string]interface{}
		err := json.Unmarshal([]byte(msgStr), &msg)
		if err != nil {
			logs.Error("解析消息失败:%s", msgStr)
			continue
		}
		messages = append(messages, msg)

		// 解析成功后，从队列中删除该消息
		err = utils.Redis.LRem(queueName, 1, msgStr).Err()
		if err != nil {
			logs.Error("删除消息失败:%s, error:%v", msgStr, err)
			// 根据具体情况，你可以选择继续处理其他消息，或者返回错误
		}
	}

	return messages, nil
}

func AssembleImVideocallCallRequest(params *dtos.CallParams, identity int, noticeUserId string, payPrice int64, anchorPrice int64, anchorId int64, isFreeCard bool, freeTime int, viewerInfo *UserCache, anchorInfo *UserCache) ImVideocallCallRequest {
	req := ImVideocallCallRequest{
		NoticeUserId: noticeUserId,
		CallType:     params.CallType,
		PayPrice:     payPrice,
		AnchorPrice:  anchorPrice,
		AnchorId:     anchorId,
		IsFreeCard:   isFreeCard,
		FreeTime:     freeTime,
		//CallerId:     params.UserId,
	}
	if identity == constants.IDENTITY_NORMAL_USER {
		fillUserDetail(&req.FromUserDetail, viewerInfo)
		fillUserDetail(&req.ToUserDetail, anchorInfo)
	} else {
		fillUserDetail(&req.FromUserDetail, anchorInfo)
		fillUserDetail(&req.ToUserDetail, viewerInfo)
	}
	return req
}

func AssembleImVideocallQuickCallRequest(noticeUserId string, payPrice int64, anchorPrice int64, anchorId int64, isFreeCard bool, freeTime int, viewerInfo *UserCache, anchorInfo *UserCache) ImVideocallCallRequest {
	req := ImVideocallCallRequest{
		NoticeUserId: noticeUserId, // 通知对方
		CallType:     constants.CALL_TYPE_RANDOM_MATCH,
		PayPrice:     payPrice,
		AnchorPrice:  anchorPrice,
		AnchorId:     anchorId,
		IsFreeCard:   isFreeCard,
		FreeTime:     freeTime,
	}

	fillUserDetail(&req.FromUserDetail, viewerInfo)
	fillUserDetail(&req.ToUserDetail, anchorInfo)

	return req
}

// AssembleImVideocallAnswerCallRequest
func AssembleImVideocallAnswerCallRequest(identity int, noticeUserId string, guid string, streamId string, homeNum string, payPrice int64, anchorPrice int64, anchorId int64, viewerInfo *UserCache, anchorInfo *UserCache) ImVideocallAnswerRequest {
	req := ImVideocallAnswerRequest{
		NoticeUserId: noticeUserId,
		Guid:         guid,
		StreamId:     streamId,
		HomeNum:      homeNum,
		AnchorId:     anchorId,
		PayPrice:     payPrice,
		AnchorPrice:  anchorPrice,
	}

	if identity == constants.IDENTITY_NORMAL_USER {
		fillUserDetail(&req.FromUserDetail, viewerInfo)
		fillUserDetail(&req.ToUserDetail, anchorInfo)
	} else {
		fillUserDetail(&req.FromUserDetail, anchorInfo)
		fillUserDetail(&req.ToUserDetail, viewerInfo)
	}

	return req
}

func AssembleImVideocallHangupRequest(identity int, noticeUserId string, guid string, anchorId int64, operationType int, callDuration int64, viewerInfo *UserCache, anchorInfo *UserCache) ImVideocallHangupRequest {
	req := ImVideocallHangupRequest{
		NoticeUserId:  noticeUserId,
		Guid:          guid,
		OperationType: operationType,
		AnchorId:      anchorId,
		CallDuration:  callDuration,
	}

	if identity == constants.IDENTITY_NORMAL_USER {
		fillUserDetail(&req.FromUserDetail, viewerInfo)
		fillUserDetail(&req.ToUserDetail, anchorInfo)
	} else {
		fillUserDetail(&req.FromUserDetail, anchorInfo)
		fillUserDetail(&req.ToUserDetail, viewerInfo)
	}

	return req
}

// 通知用户免费通话卡变更
func ImFreeCallChange(NoticeUserId string, userId string, num int, times int, remaining_num int64) (err error) {
	logs.Debug("IM 通知用户 userId:%s 快速拨打电话失败：ImUserWrongMsg(%+v)", NoticeUserId)
	postUrl := configs.WsHttpUrl
	headers := map[string]string{
		"Content-Type": "application/json",
	}
	queryParams := url.Values{}
	queryParams.Set("userid", NoticeUserId)
	queryParams.Set("service_code", "cashew_api")
	queryParams.Set("author_code", fmt.Sprintf("%d", constants.CHAT_CODE_VIDEOCALL))

	// 构建完整的请求URL，将查询参数添加到URL中
	url := postUrl + "?" + queryParams.Encode()

	dataSubCode := map[string]interface{}{
		"sub_code": constants.CHAT_CODE_FREE_CALL_CHANGE,
		"data": map[string]interface{}{
			"user_id":       userId,
			"use_card":      num,
			"times":         times,
			"remaining_num": remaining_num,
			"tag":           TAG_TYPE_NON_CALL_CHANGE,
		},
	}
	reqBody := map[string]interface{}{
		"code": constants.CHAT_CODE_VIDEOCALL,
		"data": dataSubCode,
	}

	respBody, err := HttpCall("POST", url, headers, reqBody)
	if err != nil {
		logs.Error("IM 通知用户 userId:%s 免费通话卡变更失败：ImUserWrongMsg(%+v): %v", NoticeUserId, reqBody, err)
		return
	}
	logs.Debug("响应体数据:%s", string(respBody))
	logs.Info("IM 通知用户 userId:%s 免费通话卡变更成功：ImUserAmountChangeMsg(%+v)", NoticeUserId, reqBody)
	return
}
